package cn.yangliu.jpa.handler;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.TypedQuery;

import cn.yangliu.jpa.annotations.JpaQuery;
import cn.yangliu.jpa.enums.LinkTypeEnum;
import cn.yangliu.jpa.enums.QueryTypeEnum;
import cn.yangliu.jpa.tools.JpaUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author 杨柳
 * @date 2019-06-15
 */
class JpqlHandler {

    private static final Logger log = LoggerFactory.getLogger(JpqlHandler.class);

    private JpqlHandler() {
    }

    /**
     * 构建jpal查询对象
     *
     * @param entityManagerFactory entityManagerFactory
     * @param queryPojo            查询实体对象封装
     * @param <T>                  实体类型
     * @return 查询对象
     */
    static <T> TypedQuery<T> buildJpqlQuery(EntityManagerFactory entityManagerFactory, Object queryPojo,
                                            Class<T> clazz) {
        return buildJpqlQuery(entityManagerFactory, queryPojo, clazz, null);
    }

    /**
     * 构建jpal查询对象
     *
     * @param entityManagerFactory entityManagerFactory
     * @param queryPojo            查询实体对象封装
     * @param orderAndPage         排序 和分页
     * @param <T>                  实体类型
     * @return 查询对象
     */
    static <T> TypedQuery<T> buildJpqlQuery(EntityManagerFactory entityManagerFactory, Object queryPojo,
                                            Class<T> clazz, String orderAndPage) {
        String sourceJpql = "from " + clazz.getSimpleName();

        List<Object> params = new ArrayList<>();
        List<String> fields = new ArrayList<>();

        String jpql = buildJpql(sourceJpql, queryPojo, params, fields, orderAndPage);

        log.info("excute jpql : " + jpql);

        return buildQueryParameters(entityManagerFactory, jpql, clazz, params, fields);
    }

    static <T> TypedQuery<T> buildQueryParameters(EntityManagerFactory entityManagerFactory, String jpql,
                                                  Class<T> clazz, List<Object> params, List<String> fields) {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        TypedQuery<T> query = entityManager.createQuery(jpql, clazz);
        for (int i = 0; i < fields.size(); i++) {
            String fieldName = fields.get(i);
            Object value = params.get(i);
            query.setParameter(fieldName, value);
        }
        return query;
    }

    private static String getParameter(StringBuilder sb, boolean whereFlag, Field field, JpaQuery jpaQuery,
                                       String fieldName, Object value) {
        String parameter = jpaQuery.parameter();
        LinkTypeEnum linkType = jpaQuery.link();
        QueryTypeEnum queryTypeEnum = jpaQuery.type();
        if (Objects.equals(QueryTypeEnum.LIKE, queryTypeEnum)) {
            parameter = fieldName;
            sb.append(JpaUtils.linkType(linkType, whereFlag)).append(fieldName).append("'%").append(parameter).append("%'");
        } else if (Objects.equals(QueryTypeEnum.EQ, queryTypeEnum)) {
            parameter = fieldName;
            sb.append(JpaUtils.linkType(linkType, whereFlag)).append(fieldName).append(" = :").append(parameter);
        } else if (Objects.equals(QueryTypeEnum.LT, queryTypeEnum)) {
            sb.append(JpaUtils.linkType(linkType, whereFlag)).append(fieldName).append(" < :").append(parameter);
            if (StringUtils.isBlank(parameter)) {
                parameter = field.getName();
            }
        } else if (Objects.equals(QueryTypeEnum.GT, queryTypeEnum)) {
            sb.append(JpaUtils.linkType(linkType, whereFlag)).append(fieldName).append(" > :").append(parameter);
            if (StringUtils.isBlank(parameter)) {
                parameter = field.getName();
            }
        } else if (Objects.equals(QueryTypeEnum.LTE, queryTypeEnum)) {
            if (StringUtils.isBlank(parameter)) {
                parameter = field.getName();
            }
            sb.append(JpaUtils.linkType(linkType, whereFlag)).append(fieldName).append(" <= :").append(parameter);
        } else if (Objects.equals(QueryTypeEnum.GTE, queryTypeEnum)) {
            if (StringUtils.isBlank(parameter)) {

                parameter = field.getName();
            }
            sb.append(JpaUtils.linkType(linkType, whereFlag)).append(fieldName).append(" >= :").append(parameter);
        } else if (Objects.equals(QueryTypeEnum.NOTEQ, queryTypeEnum)) {
            parameter = fieldName;
            sb.append(JpaUtils.linkType(linkType, whereFlag)).append(fieldName).append(" != :").append(parameter);
        } else if (Objects.equals(QueryTypeEnum.IN, queryTypeEnum)) {
            if (JpaUtils.isNotEmptyArray(value)) {
                sb.append(JpaUtils.linkType(linkType, whereFlag)).append(fieldName);
                JpaUtils.buildArrayParams(sb, value);
            }
        }
        return parameter;
    }


    static String buildJpql(String sourceJpql, Object queryPojo, List<Object> params, List<String> fields,
                            String orderAndPage) {
        StringBuilder sb = new StringBuilder(sourceJpql);
        List<Field> fieldList = JpaUtils.getDeclaredFields(queryPojo);

        boolean whereFlag = false;
        for (Field field : fieldList) {
            Object value = JpaUtils.getFieldValue(queryPojo, field);
            if (Objects.isNull(value) || Objects.equals(value, "")) {
                continue;
            }
            JpaQuery jpaQuery = field.getDeclaredAnnotation(JpaQuery.class);

            if (Objects.isNull(jpaQuery)) {
                continue;
            }
            params.add(value);


            if (!whereFlag) {
                sb.append(JpaUtils.WHERE);
            }

            String fieldName = jpaQuery.field();
            fieldName = StringUtils.isBlank(fieldName) ? field.getName() : fieldName;

            String parameter = getParameter(sb, whereFlag, field, jpaQuery, fieldName, value);
            whereFlag = true;
            fields.add(parameter);
        }

        if (StringUtils.isNotBlank(orderAndPage)) {
            if (!orderAndPage.contains("order by") && !orderAndPage.contains("ORDER BY")) {
                sb.append(" order by ");
            }
            sb.append(" ").append(orderAndPage);
        }

        return sb.toString();
    }

}

